package com.dbflow5.processor;

import com.dbflow5.annotation.Column;
import com.dbflow5.annotation.ColumnIgnore;
import com.dbflow5.annotation.Fts3;
import com.dbflow5.annotation.Fts4;
import com.dbflow5.annotation.Migration;
import com.dbflow5.annotation.ModelView;
import com.dbflow5.annotation.MultipleManyToMany;
import com.dbflow5.annotation.QueryModel;
import com.dbflow5.annotation.Table;
import com.dbflow5.annotation.TypeConverter;
import com.dbflow5.contentprovider.annotation.ContentProvider;
import com.dbflow5.contentprovider.annotation.TableEndpoint;
import com.dbflow5.processor.definition.DatabaseHolderDefinition;
import com.google.auto.service.AutoService;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import java.util.LinkedHashSet;
import java.util.Set;

@AutoService(Processor.class)
public class DBFlowProcessor extends AbstractProcessor {

    private ProcessorManager manager;

    /**
     * If the com.dbflow5.processor class is annotated with [ ], return an unmodifiable set with the
     * same set of strings as the annotation.  If the class is not so
     * annotated, an empty set is returned.
     *
     * @return the names of the annotation types supported by this
     * * com.dbflow5.processor, or an empty set if none
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> set = new LinkedHashSet<>();
        set.add(Table.class.getCanonicalName());
        set.add(Column.class.getCanonicalName());
        set.add(TypeConverter.class.getCanonicalName());
        set.add(ModelView.class.getCanonicalName());
        set.add(Migration.class.getCanonicalName());
        set.add(ContentProvider.class.getCanonicalName());
        set.add(TableEndpoint.class.getCanonicalName());
        set.add(ColumnIgnore.class.getCanonicalName());
        set.add(QueryModel.class.getCanonicalName());
        set.add(Fts3.class.getCanonicalName());
        set.add(Fts4.class.getCanonicalName());
        set.add(MultipleManyToMany.class.getCanonicalName());

        return set;
    }

    @Override
    public Set<String> getSupportedOptions() {
        Set<String> set = new LinkedHashSet<>();
        set.add(DatabaseHolderDefinition.OPTION_TARGET_MODULE_NAME);
        return set;
    }

    /**
     * If the com.dbflow5.processor class is annotated with [ ], return the source version in the
     * annotation.  If the class is not so annotated, [ ][javax.lang.model.SourceVersion.RELEASE_6] is returned.
     *
     * @return the latest source version supported by this com.dbflow5.processor
     */
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        manager = new ProcessorManager(processingEnv);
        manager.addHandlers(new Handlers.MigrationHandler(),
                new Handlers.TypeConverterHandler(),
                new Handlers.DatabaseHandler(),
                new Handlers.TableHandler(),
                new Handlers.QueryModelHandler(),
                new Handlers.ModelViewHandler(),
                new Handlers.ContentProviderHandler(),
                new Handlers.TableEndpointHandler());
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        manager.handle(manager, roundEnv);

        // return true if we successfully processed the Annotation.
        return true;
    }

}
